// 题意：给定一个树，统计树上距离是三的倍数的点对个数。
//
// 题解：由于(i, i)也算，(i, j), (j, i) 算两对，不过可以先统计出不计顺序的
//       不相同点对个数，计为a，最后答案就是2 * a + n。
//       然后统计时点分治，对中心点统计出模3余0, 1, 2的个数，然后算就可以，
//       要注意减去同一子树重复就行。比较难算的是模0的情况，推一下就行。
//       假设每个子树0的个数平方和为s0，最后总的0的个数为c，
//       那么对答案的贡献是(c*c - s0)/2 + c。
//
// 统计：700ms, 1.5h, 2WA
//
// run: $exec < input
// opt: 0
// flag: -g
#include <cstdio>
#include <algorithm>
#include <vector>

struct edge { int to, cost; };

int const inf = 1 << 30;
int const maxn = 20007;
bool centroid[maxn];
std::vector<edge> graph[maxn];
int subtree_size[maxn];
int n, ans;

void add_edge(int x, int y, int z)
{
	graph[x].push_back({y, z});
	graph[y].push_back({x, z});
}

int calc_subtree_size(int u, int f)
{
	int c = 1;
	for (int i = 0; i < (int)graph[u].size(); i++) {
		int v = graph[u][i].to;
		if (centroid[v] || v == f) continue;
		c += calc_subtree_size(v, u);
	}
	return subtree_size[u] = c;
}

std::pair<int, int> get_center(int u, int f, int all)
{
	std::pair<int, int> res(inf, -1);
	int max_sub = 0, sum = 1;
	for (int i = 0; i < (int)graph[u].size(); i++) {
		int v = graph[u][i].to;
		if (v == f || centroid[v]) continue;
		max_sub = std::max(max_sub, subtree_size[v]);
		sum += subtree_size[v];
		res = std::min(res, get_center(v, u, all));
	}
	max_sub = std::max(max_sub, all - sum);
	return std::min(res, std::make_pair(max_sub, u));
}

void calc_dis_to_center(int u, int f, int dis, std::vector<int> & tmp)
{
	tmp[dis]++;
	for (int i = 0; i < (int)graph[u].size(); i++) {
		int v = graph[u][i].to, c = graph[u][i].cost;
		if (centroid[v] || v == f) continue;
		calc_dis_to_center(v, u, (dis + c) % 3, tmp);
	}
}

void divide_and_conquer_on_tree(int u)
{
	int center = get_center(u, -1, calc_subtree_size(u, -1)).second;
	centroid[center] = true;

	for (int i = 0; i < (int)graph[center].size(); i++) {
		int v = graph[center][i].to;
		if (centroid[v]) continue;
		divide_and_conquer_on_tree(v);
	}

	std::vector<int> count(3);
	int repeat_0 = 0;
	for (int i = 0; i < (int)graph[center].size(); i++) {
		int v = graph[center][i].to, c = graph[center][i].cost;
		if (centroid[v]) continue;
		std::vector<int> tmp(3);
		calc_dis_to_center(v, center, c % 3, tmp);
		ans -= tmp[1] * tmp[2];
		repeat_0 += tmp[0] * tmp[0];
		for (int j = 0; j < 3; j++) count[j] += tmp[j];
	}
	ans += count[1] * count[2] + (count[0] * count[0] - repeat_0)/2 + count[0];
	centroid[center] = false;
}

int gcd(int a, int b)
{
	return (!b ? a : gcd(b, a % b));
}

void print(int a, int b)
{
	int tmp = gcd(a, b);
	a /= tmp;
	b /= tmp;
	std::printf("%d/%d\n", a, b);
}

int main()
{
	std::scanf("%d", &n);
	for (int i = 1, x, y, z; i < n; i++) {
		std::scanf("%d %d %d", &x, &y, &z);
		x--; y--;
		add_edge(x, y, z);
	}

	divide_and_conquer_on_tree(0);
	print(ans * 2 + n, n * n);
}

